package com.doangeometrydash.scene;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.andengine.engine.camera.hud.HUD;
import org.andengine.engine.handler.timer.ITimerCallback;
import org.andengine.engine.handler.timer.TimerHandler;
import org.andengine.entity.Entity;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.text.Text;
import org.andengine.extension.physics.box2d.PhysicsFactory;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.extension.tmx.TMXObject;
import org.andengine.extension.tmx.TMXObjectGroup;
import org.andengine.extension.tmx.TMXTiledMap;
import org.andengine.input.touch.TouchEvent;
import org.andengine.input.touch.detector.ContinuousHoldDetector;
import org.andengine.input.touch.detector.HoldDetector;
import org.andengine.input.touch.detector.HoldDetector.IHoldDetectorListener;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.doangeometrydash.base.BaseScene;
import com.doangeometrydash.manager.SceneManager;
import com.doangeometrydash.manager.SceneManager.SceneType;
import com.doangeometrydash.player.Player;

public class GameScene extends BaseScene implements IOnSceneTouchListener, IHoldDetectorListener
{

	private PhysicsWorld world;
	private Player player;
	private TMXTiledMap tmxMap;
	private float mapWidth;
	private float mapHeight;
	private ContinuousHoldDetector continuousHoldDetector;
	private HUD gameHUD;
	private Text txtNotify;	
	private int timeRemain = 4;
	private boolean onZone;
	private List<Entity> listEntity;
	private TimerHandler timer;

	@Override
	public void createScene()
	{
		onZone = false;
		listEntity = new ArrayList<Entity>();
		tmxMap = mResourcesManager.tmxMap;
		listEntity.add(tmxMap);
		mapWidth = tmxMap.getWidth();
		mapHeight = tmxMap.getHeight();
		attachChild(tmxMap);

		createPhysic();

		createHUD();

		if (!mResourcesManager.muteOption)
			mResourcesManager.soundTrackGame.play();

		setDelay();
	}

	private void createHUD()
	{
		gameHUD = new HUD();
		txtNotify = new Text(mCamera.getCenterX(), mCamera.getCenterY(), mResourcesManager.gameFont, "", 15, vbom);
		gameHUD.attachChild(txtNotify);
		mCamera.setHUD(gameHUD);
		listEntity.add(gameHUD);
		listEntity.add(txtNotify);
	}

	private void createPhysic()
	{
		world = new PhysicsWorld(new Vector2(0, -100), false);
		loadTMXObject();
		player = new Player(20, 275, 7.3f, 22f, vbom, mCamera, world, this);
		registerUpdateHandler(world);
		continuousHoldDetector = new ContinuousHoldDetector(this);
		registerUpdateHandler(continuousHoldDetector);
		setOnSceneTouchListener(this);
		world.setContactListener(new ContactListener()
		{

			@Override
			public void preSolve(Contact contact, Manifold oldManifold)
			{
			}

			@Override
			public void postSolve(Contact contact, ContactImpulse impulse)
			{
			}

			@Override
			public void endContact(Contact contact)
			{
				if (!onZone)
					player.setAllowJump(false);
			}

			@Override
			public void beginContact(Contact contact)
			{
				if (isBodyContact(player.getPlayerBody(), contact))
				{
					if (contact.getFixtureA().getBody().getUserData() == "wall" || contact.getFixtureB().getBody().getUserData() == "wall")
					{
						player.setAllowJump(true);
					}
					else
					{
						if (contact.getFixtureA().getBody().getUserData() == "zone" || contact.getFixtureB().getBody().getUserData() == "zone")
						{
							onZone = true;
							player.setVelocity(7f, 7);
							world.setGravity(new Vector2(0, -30));
						}
						else
							if (contact.getFixtureA().getBody().getUserData() == "goal" || contact.getFixtureB().getBody().getUserData() == "goal")
								SceneManager.getInstance().loadNextGameScene();
							else
							{
								mResourcesManager.explosion.play();
								goDie();
							}
					}
				}
			}
		});
	}

	private void goDie()
	{
		disposeScene();
		if (mResourcesManager.retryOption)
		{
			SceneManager.getInstance().loadGameSceneAgain();
		}
		else
			SceneManager.getInstance().createGameOverScene();
	}

	private void loadTMXObject()
	{
		Rectangle r;
		ArrayList<TMXObjectGroup> gr = tmxMap.getTMXObjectGroups();
		for (TMXObjectGroup objGr : gr)
		{
			if (objGr.getName().equals("wall"))
			{
				for (TMXObject o : objGr.getTMXObjects())
				{
					r = new Rectangle((o.getX() + o.getWidth() / 2), (mapHeight - o.getY() - o.getHeight() / 2), o.getWidth(), o.getHeight(), vbom);
					PhysicsFactory.createBoxBody(world, r, BodyType.StaticBody, PhysicsFactory.createFixtureDef(0, 0, 0)).setUserData("wall");
				}
				continue;
			}
			if (objGr.getName().equals("enemy"))
			{
				for (TMXObject o : objGr.getTMXObjects())
				{
					r = new Rectangle((o.getX() + o.getWidth() / 2), (mapHeight - o.getY() - o.getHeight() / 2), o.getWidth(), o.getHeight(), vbom);
					PhysicsFactory.createBoxBody(world, r, BodyType.StaticBody, PhysicsFactory.createFixtureDef(0, 0, 0)).setUserData("enemy");
				}
				continue;
			}
			if (objGr.getName().equals("zone"))
			{
				for (TMXObject o : objGr.getTMXObjects())
				{
					r = new Rectangle((o.getX() + o.getWidth() / 2), (mapHeight - o.getY() - o.getHeight() / 2), o.getWidth(), o.getHeight(), vbom);
					PhysicsFactory.createBoxBody(world, r, BodyType.KinematicBody, PhysicsFactory.createFixtureDef(0, 0, 0)).setUserData("zone");
				}
				continue;
			}
			if (objGr.getName().equals("goal"))
			{
				for (TMXObject o : objGr.getTMXObjects())
				{
					r = new Rectangle((o.getX() + o.getWidth() / 2), (mapHeight - o.getY() - o.getHeight() / 2), o.getWidth(), o.getHeight(), vbom);
					PhysicsFactory.createBoxBody(world, r, BodyType.StaticBody, PhysicsFactory.createFixtureDef(0, 0, 0)).setUserData("goal");
				}
				continue;
			}
		}

		mCamera.setBounds(0, 0, mapWidth, mapHeight);
		mCamera.setBoundsEnabled(true);
	}

	@Override
	public void onBackKeyPressed()
	{
		disposeScene();
		SceneManager.getInstance().loadMenuScene();
	}

	@Override
	public SceneType getSceneType()
	{
		return SceneType.SCENE_GAME;
	}

	@Override
	public void disposeScene()
	{
		mEngine.runOnUpdateThread(new Runnable()
		{
			@Override
			public void run()
			{
				mCamera.setBoundsEnabled(false);
				mCamera.setChaseEntity(null);
				mCamera.setCenter(240, 160);
				mResourcesManager.soundTrackGame.pause();
				mResourcesManager.soundTrackGame.seekTo(0);

				for (Entity e : listEntity)
				{
					e.clearEntityModifiers();
					e.clearUpdateHandlers();
					e.detachSelf();
					if (!e.isDisposed())
						e.dispose();
				}
				clearPhysicsWorld();
				clearTouchAreas();
				clearUpdateHandlers();
				System.gc();
			}
		});
	}

	private void clearPhysicsWorld()
	{
		if (world != null)
		{
			Iterator<Body> b = world.getBodies();
			while (true)
			{
				if (!b.hasNext())
				{
					world.clearForces();
					world.clearPhysicsConnectors();
					world.reset();
					world.dispose();
					world = null;
					return;
				}
				try
				{
					world.destroyBody(b.next());
				}
				catch (Exception ex)
				{

				}
			}
		}
	}

	private boolean isBodyContact(Body b, Contact c)
	{
		if (c.getFixtureA().getBody().equals(b) || c.getFixtureB().getBody().equals(b))
			return true;
		return false;
	}

	@Override
	public void onHoldStarted(HoldDetector pHoldDetector, int pPointerID, float pHoldX, float pHoldY)
	{
	}

	@Override
	public void onHold(HoldDetector pHoldDetector, long pHoldTimeMilliseconds, int pPointerID, float pHoldX, float pHoldY)
	{
		player.jump();
	}

	@Override
	public void onHoldFinished(HoldDetector pHoldDetector, long pHoldTimeMilliseconds, int pPointerID, float pHoldX, float pHoldY)
	{
	}

	@Override
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent)
	{
		if (pSceneTouchEvent.isActionDown())
		{
			player.jump();
		}
		continuousHoldDetector.onTouchEvent(pSceneTouchEvent);
		return true;
	}

	private void setDelay()
	{
		mEngine.registerUpdateHandler(timer = new TimerHandler(1, new ITimerCallback()
		{
			@Override
			public void onTimePassed(TimerHandler pTimerHandler)
			{
				if (timeRemain > 1)
				{
					txtNotify.setText(Integer.toString(timeRemain - 1));
					timeRemain--;
					timer.reset();
				}
				else
				{
					if (timeRemain == 1)
					{
						txtNotify.setText("GO!");
						timeRemain--;
						timer.reset();
						player.setAllowRun(true);
					}
					else
					{
						txtNotify.setText("");						
					}
				}
			}
		}));
	}
}
